#ifndef JCONCURRENT_MAP_H
#define JCONCURRENT_MAP_H

#include "jconcurrent_global.h"
#include "jconcurrentmapkernel.h"
#include "jconcurrentreducekernel.h"
#include "jconcurrentfunctionwrappers.h"

namespace JConcurrent {

// map() on sequences
template <typename Sequence, typename MapFunctor>
JFuture<void> map(Sequence &sequence, MapFunctor map)
{
    return startMap(sequence.begin(), sequence.end(), JConcurrentPrivate::createFunctionWrapper(map));
}

// map() on iterators
template <typename Iterator, typename MapFunctor>
JFuture<void> map(Iterator begin, Iterator end, MapFunctor map)
{
    return startMap(begin, end, JConcurrentPrivate::createFunctionWrapper(map));
}

// mappedReduced() for sequences.
template <typename ResultType, typename Sequence, typename MapFunctor, typename ReduceFunctor>
JFuture<ResultType> mappedReduced(const Sequence &sequence,
                                  MapFunctor map,
                                  ReduceFunctor reduce,
                                  ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce))
{
    return startMappedReduced<typename JConcurrentPrivate::MapResultType<void, MapFunctor>::ResultType, ResultType>
            (sequence,
             JConcurrentPrivate::createFunctionWrapper(map),
             JConcurrentPrivate::createFunctionWrapper(reduce),
             options);
}

template <typename Sequence, typename MapFunctor, typename ReduceFunctor>
JFuture<typename JConcurrentPrivate::ReduceResultType<ReduceFunctor>::ResultType> mappedReduced(const Sequence &sequence,
                                                                                                MapFunctor map,
                                                                                                ReduceFunctor reduce,
                                                                                                ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce))
{
    return startMappedReduced<typename JConcurrentPrivate::MapResultType<void, MapFunctor>::ResultType, typename JConcurrentPrivate::ReduceResultType<ReduceFunctor>::ResultType>
            (sequence,
             JConcurrentPrivate::createFunctionWrapper(map),
             JConcurrentPrivate::createFunctionWrapper(reduce),
             options);
}

// mappedReduced() for iterators
template <typename ResultType, typename Iterator, typename MapFunctor, typename ReduceFunctor>
JFuture<ResultType> mappedReduced(Iterator begin,
                                  Iterator end,
                                  MapFunctor map,
                                  ReduceFunctor reduce,
                                  ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce))
{
    return startMappedReduced<typename JConcurrentPrivate::MapResultType<void, MapFunctor>::ResultType, ResultType>
            (begin, end,
             JConcurrentPrivate::createFunctionWrapper(map),
             JConcurrentPrivate::createFunctionWrapper(reduce),
             options);
}

template <typename Iterator, typename MapFunctor, typename ReduceFunctor>
JFuture<typename JConcurrentPrivate::ReduceResultType<ReduceFunctor>::ResultType> mappedReduced(Iterator begin,
                                                                                                Iterator end,
                                                                                                MapFunctor map,
                                                                                                ReduceFunctor reduce,
                                                                                                ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce))
{
    return startMappedReduced<typename JConcurrentPrivate::MapResultType<void, MapFunctor>::ResultType, typename JConcurrentPrivate::ReduceResultType<ReduceFunctor>::ResultType>
            (begin, end,
             JConcurrentPrivate::createFunctionWrapper(map),
             JConcurrentPrivate::createFunctionWrapper(reduce),
             options);
}

// mapped() for sequences
template <typename Sequence, typename MapFunctor>
JFuture<typename JConcurrentPrivate::MapResultType<void, MapFunctor>::ResultType> mapped(const Sequence &sequence, MapFunctor map)
{
    return startMapped<typename JConcurrentPrivate::MapResultType<void, MapFunctor>::ResultType>(sequence, JConcurrentPrivate::createFunctionWrapper(map));
}

// mapped() for iterator ranges.
template <typename Iterator, typename MapFunctor>
JFuture<typename JConcurrentPrivate::MapResultType<void, MapFunctor>::ResultType> mapped(Iterator begin, Iterator end, MapFunctor map)
{
    return startMapped<typename JConcurrentPrivate::MapResultType<void, MapFunctor>::ResultType>(begin, end, JConcurrentPrivate::createFunctionWrapper(map));
}

// blockingMap() for sequences
template <typename Sequence, typename MapFunctor>
void blockingMap(Sequence &sequence, MapFunctor map)
{
    startMap(sequence.begin(), sequence.end(), JConcurrentPrivate::createFunctionWrapper(map)).startBlocking();
}

// blockingMap() for iterator ranges
template <typename Iterator, typename MapFunctor>
void blockingMap(Iterator begin, Iterator end, MapFunctor map)
{
    startMap(begin, end, JConcurrentPrivate::createFunctionWrapper(map)).startBlocking();
}

// blockingMappedReduced() for sequences
template <typename ResultType, typename Sequence, typename MapFunctor, typename ReduceFunctor>
ResultType blockingMappedReduced(const Sequence &sequence,
                                 MapFunctor map,
                                 ReduceFunctor reduce,
                                 ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce))
{
    return JConcurrent::startMappedReduced<typename JConcurrentPrivate::MapResultType<void, MapFunctor>::ResultType, ResultType>
            (sequence,
             JConcurrentPrivate::createFunctionWrapper(map),
             JConcurrentPrivate::createFunctionWrapper(reduce),
             options)
            .startBlocking();
}

template <typename MapFunctor, typename ReduceFunctor, typename Sequence>
typename JConcurrentPrivate::ReduceResultType<ReduceFunctor>::ResultType blockingMappedReduced(const Sequence &sequence,
                                                                                               MapFunctor map,
                                                                                               ReduceFunctor reduce,
                                                                                               ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce))
{
    return JConcurrent::startMappedReduced<typename JConcurrentPrivate::MapResultType<void, MapFunctor>::ResultType, typename JConcurrentPrivate::ReduceResultType<ReduceFunctor>::ResultType>
            (sequence,
             JConcurrentPrivate::createFunctionWrapper(map),
             JConcurrentPrivate::createFunctionWrapper(reduce),
             options)
            .startBlocking();
}

// blockingMappedReduced() for iterator ranges
template <typename ResultType, typename Iterator, typename MapFunctor, typename ReduceFunctor>
ResultType blockingMappedReduced(Iterator begin,
                                 Iterator end,
                                 MapFunctor map,
                                 ReduceFunctor reduce,
                                 JConcurrent::ReduceOptions options = JConcurrent::ReduceOptions(JConcurrent::UnorderedReduce | JConcurrent::SequentialReduce))
{
    return JConcurrent::startMappedReduced<typename JConcurrentPrivate::MapResultType<void, MapFunctor>::ResultType, ResultType>
            (begin, end,
             JConcurrentPrivate::createFunctionWrapper(map),
             JConcurrentPrivate::createFunctionWrapper(reduce),
             options)
            .startBlocking();
}

template <typename Iterator, typename MapFunctor, typename ReduceFunctor>
typename JConcurrentPrivate::ReduceResultType<ReduceFunctor>::ResultType blockingMappedReduced(Iterator begin,
                                                                                               Iterator end,
                                                                                               MapFunctor map,
                                                                                               ReduceFunctor reduce,
                                                                                               JConcurrent::ReduceOptions options = JConcurrent::ReduceOptions(JConcurrent::UnorderedReduce | JConcurrent::SequentialReduce))
{
    return JConcurrent::startMappedReduced<typename JConcurrentPrivate::MapResultType<void, MapFunctor>::ResultType, typename JConcurrentPrivate::ReduceResultType<ReduceFunctor>::ResultType>
            (begin, end,
             JConcurrentPrivate::createFunctionWrapper(map),
             JConcurrentPrivate::createFunctionWrapper(reduce),
             options)
            .startBlocking();
}

// mapped() for sequences with a different putput sequence type.
template <typename OutputSequence, typename InputSequence, typename MapFunctor>
OutputSequence blockingMapped(const InputSequence &sequence, MapFunctor map)
{
    return blockingMappedReduced<OutputSequence>
            (sequence,
             JConcurrentPrivate::createFunctionWrapper(map),
             JConcurrentPrivate::PushBackWrapper(),
             JConcurrent::OrderedReduce);
}

template <typename MapFunctor, typename InputSequence>
typename JConcurrentPrivate::MapResultType<InputSequence, MapFunctor>::ResultType blockingMapped(const InputSequence &sequence, MapFunctor map)
{
    typedef typename JConcurrentPrivate::MapResultType<InputSequence, MapFunctor>::ResultType OutputSequence;
    return blockingMappedReduced<OutputSequence>
            (sequence,
             JConcurrentPrivate::createFunctionWrapper(map),
             JConcurrentPrivate::PushBackWrapper(),
             JConcurrent::OrderedReduce);
}

// mapped()  for iterator ranges
template <typename Sequence, typename Iterator, typename MapFunctor>
Sequence blockingMapped(Iterator begin, Iterator end, MapFunctor map)
{
    return blockingMappedReduced<Sequence>
            (begin, end,
             JConcurrentPrivate::createFunctionWrapper(map),
             JConcurrentPrivate::PushBackWrapper(),
             JConcurrent::OrderedReduce);
}

template <typename Iterator, typename MapFunctor>
typename JConcurrentPrivate::MapResultType<Iterator, MapFunctor>::ResultType blockingMapped(Iterator begin, Iterator end, MapFunctor map)
{
    typedef typename JConcurrentPrivate::MapResultType<Iterator, MapFunctor>::ResultType OutputSequence;
    return blockingMappedReduced<OutputSequence>
            (begin, end,
             JConcurrentPrivate::createFunctionWrapper(map),
             JConcurrentPrivate::PushBackWrapper(),
             JConcurrent::OrderedReduce);
}

} // namespace JConcurrent

#endif  // JCONCURRENT_MAP_H
